/*
 * Copyright (c) 2018-2024 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "subcmd.h"

static int queueReqCode[] =
{
    0, 0, QUEUE_OPEN, QUEUE_CLOSED, QUEUE_ACTIVATE, QUEUE_INACTIVATE,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    QUEUE_NODEADD, QUEUE_NODEREMOVE, QUEUE_NODEUPDATE, 0,
    0, 0
};

/**
 * @brief       qp admin handler, to manage queue in the cluster.
 *
 * @param[in]   argc          #1: number of input argument
 * @param[in]   argv          #2: string array of the input argument
 * @param[in]   index         #3: index of the control: open, close, act, inact,
 *                                nodeadd, noderemove or nodeupdate.
 *
 * @retval      0             #1: succeed
 * @retval      1             #2: input option is wrong or kind of input argument not specified
 * @retval      2             #3: argument has syntax error
 * @retval      3             #4: failed to manage the queue in the cluster
 *
 */
int
cmd_manage_queue(int argc, char *argv[], admin_control_t index)
{
    const char *options;
    static struct option long_options[3];

    long_options[0].name = "help";
    long_options[0].has_arg = no_argument;
    long_options[0].flag = 0;
    long_options[0].val = 'h';

    switch (index) {
    case ADMIN_QUEUE_OPEN:
    case ADMIN_QUEUE_CLOSE:
    case ADMIN_QUEUE_ACT:
    case ADMIN_QUEUE_INACT:
        options = "h";

        long_options[1].name = NULL;
        long_options[1].has_arg = no_argument;
        long_options[1].flag = 0;
        long_options[1].val = 0;

        break;
    case ADMIN_QUEUE_NODEADD:
    case ADMIN_QUEUE_NODEREMOVE:
    case ADMIN_QUEUE_NODEUPDATE:
        options = "m:h";

        long_options[1].name = "node";
        long_options[1].has_arg = required_argument;
        long_options[1].flag = 0;
        long_options[1].val = 'm';

        long_options[2].name = NULL;
        long_options[2].has_arg = no_argument;
        long_options[2].flag = 0;
        long_options[2].val = 0;

        break;
    default:
        fprintf(stderr, "Unknown manage queue subcmd specified.\n");

        admin_usage(index);
        exit(1);
    }

    char *strNode=NULL;
    while (TRUE) {

        int option_index = 0;
        int opt;
        opt = getopt_long(argc, argv, options, long_options, &option_index);
        if (-1 == opt) {
            break;
        }

        switch (opt) {
        case 'm':

            strNode = copy_string(optarg);
            
            if (strlen(strNode) <= 0) {
                fprintf(stderr, "Must specify a node name for -m/--node option.\n");
                admin_usage(index);
                exit(2);
            }

            break;
        case 'h':
            admin_usage(index);
            exit(0);
        case '?':
        default:
            admin_usage(index);
            exit(1);
        }
    }

    unsigned int numQueue, queueOptions=0;
    int all=FALSE;
    char **queues=NULL, **queuePoint=NULL;
    numQueue = get_argument_names(argc, argv, optind, &queues, &all, "queueC");
    if (!all && 0 != numQueue) {
        queuePoint = queues;
    } else {
        queueOptions |= ALL_QUEUE;
    }

    switch (index) {
    case ADMIN_QUEUE_NODEADD:
    case ADMIN_QUEUE_NODEREMOVE:
    case ADMIN_QUEUE_NODEUPDATE:
        if (NULL == strNode) {
            fprintf(stderr, "Must specify a node name for -m/--node option.\n");
            admin_usage(index);
            exit(1);
        }

        if (!all && 0 == numQueue) {
            fprintf(stderr, "Must specify a queue name.\n");
            admin_usage(index);
            exit(1);
        }
        break;
    default:
        break;
    }

    struct request queueRequest;
    memset(&queueRequest, 0, sizeof(struct request));

    unsigned int n = 0;
    queueRequest.keyvalues = (struct keyvalue *)calloc(5, sizeof(struct keyvalue));
    if (NULL == queueRequest.keyvalues) {
        fprintf(stderr, "calloc(%ld) failed due to %m.\n", 5*sizeof(struct keyvalue));
        exit(4);
    }

    queueRequest.keyvalues[n].key = REQUEST_KEY_OPTIONS;
    queueRequest.keyvalues[n].value = (char *)calloc(12, sizeof(char));
    if (NULL == queueRequest.keyvalues[n].value) {
        fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
        exit(4);
    }
    snprintf(queueRequest.keyvalues[n].value, 12, "%d", queueOptions);
    n ++;

    if (0 < numQueue) {

        // Specified queue names.
        queueRequest.keyvalues[n].key = REQUEST_KEY_NUMBER;
        queueRequest.keyvalues[n].value = (char *)calloc(12, sizeof(char));
        if (NULL == queueRequest.keyvalues[n].value) {
            fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
            exit(4);
        }
        snprintf(queueRequest.keyvalues[n].value, 12, "%d", numQueue);
        n ++;

        queueRequest.keyvalues[n].key = REQUEST_KEY_NAMES;
        unsigned int i, size = 0;
        for (i = 0; i < numQueue; i++) {
            size += strlen(queuePoint[i]) + 1;
        }
        queueRequest.keyvalues[n].value = (char *)calloc(size, sizeof(char));
        if (NULL == queueRequest.keyvalues[n].value) {
            fprintf(stderr, "calloc(%d) failed due to %m.\n", size);
            exit(4);
        }

        for (i = 0; i < numQueue; i++) {
            if (i == 0) {
                strcpy(queueRequest.keyvalues[n].value, queuePoint[i]);
            } else {
                strcat(queueRequest.keyvalues[n].value, " ");
                strcat(queueRequest.keyvalues[n].value, queuePoint[i]);
            }
        }
        n ++;
    }
    queueRequest.number = n;

    // Call JS to query queue list.
    struct QuickPoolQueueReply *queueReply;
    queueReply = client_get_queueinfo(&queueRequest, NULL);
    if (quickpool_errno != 0 || NULL == queueReply) {

        if (NULL == queueReply) {
            fprintf(stderr, "Fail to control queue due to above error.\n");
            gf_xdr_free(gf_xdr_request, &queueRequest);
            FREEUP(queues);
            exit(3);
        }

        fprintf(stderr, "%s\n", queueReply->error_message);
        gf_xdr_free(gf_xdr_request, &queueRequest);
        gf_xdr_free(libxdr_queue_reply, queueReply);
        FREEUP(queueReply);
        FREEUP(queues);

        exit(3);
    }
    gf_xdr_free(gf_xdr_request, &queueRequest);
    FREEUP(queues);

    int reqCode = queueReqCode[index];

    struct request controlRequest;
    memset(&controlRequest, 0, sizeof(struct request));

    if (reqCode == QUEUE_NODEADD || reqCode == QUEUE_NODEREMOVE || reqCode == QUEUE_NODEUPDATE) {
        controlRequest.number = 3;
    } else {
        controlRequest.number = 2;
    }

    controlRequest.keyvalues = (struct keyvalue *)calloc(controlRequest.number,
                                                         sizeof(struct keyvalue));
    if (NULL == controlRequest.keyvalues) {
        fprintf(stderr, "calloc(%ld) failed due to %m.\n",
                controlRequest.number*sizeof(struct keyvalue));
        exit(4);
    }

    controlRequest.keyvalues[0].key = REQUEST_KEY_REQUEST_CODE;
    controlRequest.keyvalues[0].value = (char *)calloc(12, sizeof(char));
    if (NULL == controlRequest.keyvalues[n].value) {
        fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
        exit(4);
    }
    snprintf(controlRequest.keyvalues[0].value, 12, "%d", reqCode);

    controlRequest.keyvalues[1].key = REQUEST_KEY_NAMES;

    if (reqCode == QUEUE_NODEADD || reqCode == QUEUE_NODEREMOVE || reqCode == QUEUE_NODEUPDATE) {

        controlRequest.keyvalues[2].key = REQUEST_KEY_HOSTNAME;   
        controlRequest.keyvalues[2].value = copy_string(strNode);
    }

    unsigned int i;
    for (i = 0; i < queueReply->num_queue; i++) {
        // Trying to do operation on queue one by one.

        char *strQueue = queueReply->queues[i].queue;
        FREEUP(controlRequest.keyvalues[1].value);
        controlRequest.keyvalues[1].value = copy_string(strQueue);

        // call JS to do the control operation.
        if (client_control_queue(&controlRequest, NULL) < 0) {
            continue;
        }

        switch (reqCode) {
        case QUEUE_OPEN:
            printf("Queue <%s> is opened\n", strQueue);
            break;
        case QUEUE_CLOSED:
            printf("Queue <%s> is closed\n", strQueue);
            break;
        case QUEUE_ACTIVATE:
	    printf("Queue <%s> is activated\n", strQueue);
            break;
        case QUEUE_INACTIVATE:
            printf("Queue <%s> is inactivated\n", strQueue);
            break;
        case QUEUE_NODEADD:
            printf("Queue <%s> has add nodes <%s>\n", strQueue, strNode);
            break;
        case QUEUE_NODEREMOVE:
            printf("Queue <%s> has remove host <%s>\n", strQueue, strNode);
            break;
        case QUEUE_NODEUPDATE:
            printf("Queue <%s> has update the node list to <%s>\n", strQueue, strNode);
            break;            
        default:
            fprintf(stderr, "Command internal error: unkown reqcode %d\n", reqCode);
        }
    }

    gf_xdr_free(gf_xdr_request, &controlRequest);
    gf_xdr_free(libxdr_queue_reply, queueReply);
    FREEUP(queueReply);

    return 0;
} // end function cmd_manage_queue
